package middleware

import (
	"context"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/metrics"
	"github.com/pkg/errors"
	"golang.org/x/time/rate"
	"http_rest/register/service"
	"time"
)

var ErrLimitExceed = errors.New("Rate limit exceed!")

func NewTokenBucketLimiterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware{
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			if !bkt.Allow(){
				return nil, ErrLimitExceed
			}
			return next(ctx, request)
		}
	}
}

type metricMiddleware struct{
	service.Service
	requestCount metrics.Counter
	requestLatency metrics.Histogram
}

func Metrics(requestCount metrics.Counter, requestLatency metrics.Histogram) service.ServiceMiddleware{
	return func(service service.Service) service.Service {
		return metricMiddleware{
			service,
			requestCount,
			requestLatency,
		}
	}
}

func (mw metricMiddleware) Add(a, b int) (ret int){
	defer func(beign time.Time) {
		lvs := []string{"method", "Add"}
		mw.requestCount.With(lvs...).Add(1)
		mw.requestLatency.With(lvs...).Observe(time.Since(beign).Seconds())
	}(time.Now())
	ret = mw.Service.Add(a, b)
	return ret
}

func (mw metricMiddleware) Subtract(a, b int) (ret int){
	defer func(beign time.Time) {
		lvs := []string{"method", "Subtract"}
		mw.requestCount.With(lvs...).Add(1)
		mw.requestLatency.With(lvs...).Observe(time.Since(beign).Seconds())
	}(time.Now())
	ret = mw.Service.Add(a, b)
	return ret
}

func (mw metricMiddleware) Multiply(a, b int) (ret int){
	defer func(beign time.Time) {
		lvs := []string{"method", "Multiply"}
		mw.requestCount.With(lvs...).Add(1)
		mw.requestLatency.With(lvs...).Observe(time.Since(beign).Seconds())
	}(time.Now())
	ret = mw.Service.Add(a, b)
	return ret
}

func (mw metricMiddleware) Divide(a, b int) (ret int, err error){
	defer func(beign time.Time) {
		lvs := []string{"method", "Divide"}
		mw.requestCount.With(lvs...).Add(1)
		mw.requestLatency.With(lvs...).Observe(time.Since(beign).Seconds())
	}(time.Now())
	ret, err = mw.Service.Divide(a, b)
	return ret, err
}

func (mw  metricMiddleware) HealthCheck() (result bool){
	defer func(begin time.Time) {
		lvs := []string{"method", "HealthCheck"}
		mw.requestCount.With(lvs...).Add(1)
		mw.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
	}(time.Now())
	result = mw.Service.HealthCheck()
	return
}